package cn.dengta.context.config;

import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import cn.dengta.common.context.*;
import cn.dengta.common.web.mvc.*;
import cn.dengta.context.auth.*;
import cn.dengta.context.model.AppContext;
import cn.dengta.context.model.WesKit;
import cn.dengta.context.web.*;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import me.codeplayer.util.JSONUtil;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.http.MediaType;
import org.springframework.http.converter.*;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.RequestToViewNameTranslator;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.function.support.RouterFunctionMapping;
import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping;
import org.springframework.web.servlet.resource.ResourceUrlProvider;

@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 600) // 启用基于 Servlet 的 Spring Session
@EnableConfigurationProperties(CryptoSuiteContext.class)
@Slf4j
public class WebMvcConfig extends DelegatingWebMvcConfiguration {

	/** API 服务器的根路径，形如："https://api.flala6.com" */
	private static String apiRoot;
	private static String inviteRoot;

	@Override
	public BeanNameUrlHandlerMapping beanNameHandlerMapping(FormattingConversionService conversionService,
	                                                        ResourceUrlProvider resourceUrlProvider) {
		return null;
	}

	@Override
	public RouterFunctionMapping routerFunctionMapping(FormattingConversionService conversionService,
	                                                   ResourceUrlProvider resourceUrlProvider) {
		return null;
	}

	/**
	 * 当找不到对应的请求方法时，用 Tomcat 等容器默认的 DefaultServletHandler 进行后续处理
	 */
	@Override
	public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
		configurer.enable("default");
	}

	/**
	 * 请求路径匹配逻辑定制
	 */
	@Override
	protected void configurePathMatch(PathMatchConfigurer configurer) {
		super.configurePathMatch(configurer);
		// "/user/" 和 "/user" 【不】视为相同的路径
		configurer.setUseTrailingSlashMatch(false);
	}

	/**
	 * 跨域配置
	 */
	@Override
	public void addCorsMappings(CorsRegistry registry) {
		boolean all = Env.inLocal() || Env.inner();
		log.info("跨域配置: 应用环境={}, 当前环境={}, all={}", AppContext.current.label, Env.CURRENT.label, all);
		final CorsRegistration cors = registry.addMapping("/**");
		if (all) {
			cors.allowedOriginPatterns(CorsConfiguration.ALL);
		} else {
			// 国内： http://uat.guangxijunxuan.cn/api  一个域名，多个路径
			// 海外： http://uat-api.flala6.com/  多个子域名
			List<String> domainList = new ArrayList<>();
			for (WesKit wesKit : WesKit.CACHE) {
				if (wesKit.isMain()) {
					continue;
				}
				String domainPrefix = WesKit.getDomainPrefix(wesKit, !Env.inProduction());
				if (StringUtil.isNotEmpty(domainPrefix)) {
					domainList.add("https://" + (Env.inProduction() ? "" : Env.CURRENT.value) + domainPrefix);
				}
			}
			log.info("跨域配置：{}", JSONUtil.encode(domainList));
			if (!AppContext.inter()) {
				final String prefix = envSubdomainPrefix();
				String url = ".flala6.com";
				domainList = Stream.of("api", "invite", "manager", "agent", "agent-manager", "game").map(p -> "https://" + prefix + p + url).collect(Collectors.toList());
				domainList.add("https://" + prefix + "www.fcash6.com");
				domainList.add("https://flala6.com");
				domainList.add("http://" + (Env.inProduction() ? "" : prefix) + "invite.hkdengta.com"); // 只有 生产 和 测试 两个
				domainList.add("https://" + prefix + "anchor.tooyou8.com");
			}
			domainList.add("https://akusw.iopss33.com");
			domainList.add("https://flala.guangxijunxuan.cn");
			domainList.add("https://liaoyu.guangxijunxuan.cn");
			domainList.add("https://pay.guangxijunxuan.cn");
			domainList.add("https://akusw.iopss33.com");
			cors.allowedOriginPatterns(domainList.toArray(String[]::new));
		}
		cors.allowedMethods("*");
		cors.allowedHeaders("Authorization", Context.internal().getAppIdHeaderName(),
						"Ori-" + Context.internal().getAppIdHeaderName(),
						"User-Agent", "Accept-Language", "Route-Path", "Dt-Timestamp", "Dt-Sign")
				.allowCredentials(true)
				.maxAge(3600 /* 默认 1800s */);
	}

	/**
	 * 过滤器：请求上下文
	 * 【注意】所有 自定义 Filter 的 order 都必须 大于 {@link org.springframework.session.web.http.SessionRepositoryFilter#DEFAULT_ORDER }
	 */
	@Bean
	public FilterRegistrationBean<RequestContextFilter> requestContextFilter() {
		// EnhanceHttpServletRequestWrapper.enableAppIdEncrypt(null);
		FilterRegistrationBean<RequestContextFilter> bean = new FilterRegistrationBean<>(new RequestContextFilter());
		bean.setName("requestContextFilter");
		bean.addUrlPatterns("/*");
		bean.setOrder(org.springframework.session.web.http.SessionRepositoryFilter.DEFAULT_ORDER + 100);
		return bean;
	}

	/**
	 * 过滤器：健康检查
	 * <p>
	 * 【注意】所有 自定义 Filter 的 order 都必须 大于 {@link org.springframework.session.web.http.SessionRepositoryFilter#DEFAULT_ORDER }
	 */
	@Bean
	public FilterRegistrationBean<ActuatorFilter> actuatorFilter() {
		FilterRegistrationBean<ActuatorFilter> bean = new FilterRegistrationBean<>(new ActuatorFilter());
		bean.setName("actuatorFilter");
		bean.addUrlPatterns(ActuatorFilter.URL_PATTERN);
		bean.setOrder(0);
		return bean;
	}

	@Bean
	public AutoLoginHandler autoLoginHandler() {
		return new DefaultAutoLoginHandler();
	}

	/** 拦截器 */
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(new UserSessionInterceptor(autoLoginHandler()));
		registry.addInterceptor(new PermissionInterceptor());
		registry.addInterceptor(new ExportInterceptor());
	}

	/**
	 * 参数转换器
	 */
	@Override
	public void addFormatters(FormatterRegistry registry) {
		registry.addConverter(new DateConverter());
		registry.addConverter(new UserIdConverter());
		registry.addConverter(new ImageConverter());
	}

	/**
	 * 使用 fastjson 替换 默认的 jackson
	 */
	@Override
	protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
		FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
		converter.setDefaultCharset(StandardCharsets.UTF_8);
		FastJsonConfig config = new FastJsonConfig();
		config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
		config.setSerializeFilters(UserIdEncodeFilter.INSTANCE);
		converter.setFastJsonConfig(config);
		converter.setSupportedMediaTypes(Arrays.asList(
				MediaType.APPLICATION_JSON
				, MediaType.TEXT_HTML
		));
		converters.add(converter);
		converters.add(new ByteArrayHttpMessageConverter()); // 要兼容支持 prometheus 监控
		converters.add(new StringHttpMessageConverter()); // 要兼容支持 prometheus 监控
	}

	/**
	 * 处理器方法的自定义参数解析器
	 */
	@Override
	public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
		resolvers.add(new CustomHandlerMethodArgumentResolver());
	}

	/**
	 * 屏蔽默认的 DefaultRequestToViewNameTranslator。不然 返回 void 类型的 请求方法将会根据请求路径进行视图渲染
	 */
	@Bean
	public RequestToViewNameTranslator viewNameTranslator() {
		return new NullViewNameTranslator();
	}

	/** 异常处理 */
	@Bean
	public EnhanceExceptionResolver exceptionResolver() {
		return new EnhanceExceptionResolver();
	}

	/** 获取当前环境的子域名前缀。例如：""（生产环境）、"dev-"（开发环境）、"test-"（测试环境） */
	public static String envSubdomainPrefix() {
		return envSubdomainPrefix(Env.CURRENT);
	}

	public static String envSubdomainPrefix2() {
		Env env = Env.CURRENT;
		return env == Env.PROD ? "" : env.value + ".";
	}

	/** 获取指定环境的子域名前缀。例如：""（生产环境）、"dev-"（开发环境）、"test-"（测试环境） */
	public static String envSubdomainPrefix(Env env) {
		return env == Env.PROD ? "" : (env.value + (AppContext.inter() ? "." : "-"));
	}

	/** 新的 */
	public static String envSubdomainPrefix(WesKit wesKit) {
		return envSubdomainPrefix(Env.CURRENT, wesKit);
	}

	public static String envSubdomainPrefix(Env env, WesKit wesKit) {
		return env == Env.PROD ? "" : (env.value + (wesKit.isMain() ? "-" : "."));
	}

	/** 获取 API 服务器的根路径，形如："https://api.flala6.com" */
	public static String getApiRoot(WesKit wesKit) {
		wesKit = wesKit == null ? WesKit.ofWesKit() : wesKit;
		final String url = wesKit.isMain() ? "api.flala6.com" : WesKit.getDomainPrefix(wesKit, false);
		final String prefix = envSubdomainPrefix(wesKit);
		return "https://" + prefix + url;
	}

	public static String getApiRoot() {
		return getApiRoot(null);
	}

	/** 获取 API 服务器的根路径，形如："https://invite.flala6.com" */
	public static String getInviteRoot(WesKit wesKit) {
		String root = inviteRoot;
		if (root == null) {
			final String prefix = envSubdomainPrefix(wesKit);
			String url = wesKit.isMain() ? "invite.flala6.com" : WesKit.getDomainPrefix(wesKit, false);
			inviteRoot = root = "https://" + prefix + url;
		}
		return root;
	}

	@Bean
	public CookieSerializer cookieSerializer() {
		DefaultCookieSerializer serializer = new DefaultCookieSerializer();
		if (!Env.inProduction()) {
			serializer.setSameSite(null);
		}
		serializer.setCookiePath("/");
		return serializer;
	}

}
